package com.zhuoyue.biz.core.api.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhuoyue.biz.core.api.dao.AppUserDao;
import com.zhuoyue.biz.core.api.domain.AppUserDO;
import com.zhuoyue.biz.core.api.domain.HomeDO;
import com.zhuoyue.biz.core.api.dto.rep.AppUserRepDTO;
import com.zhuoyue.biz.core.api.service.JpushService;
import com.zhuoyue.common.aop.auth.User;
import com.zhuoyue.common.constant.ExtrasCode;
import com.zhuoyue.common.constant.MessageCode;
import com.zhuoyue.common.exception.ErrorType;
import com.zhuoyue.common.exception.ZyServerException;
import com.zhuoyue.common.jwt.util.JwtOperator;
import com.zhuoyue.common.utils.SmsTpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author wjming
 * @ClassName: AppUserService
 * @Description: 用户基本操作逻辑类
 * @Date 2021-06-28 12:48
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class AppUserService extends ServiceImpl<AppUserDao, AppUserDO> {
    private final AppUserDao appUserDao;
    private final JwtOperator jwtOperator;
    private final RedisTemplate redisTemplate;
    private final JpushService jPushService;
    private final HomeService homeService;

    /**
     * @Description: 用户注册
     * @param: requestUserDO
     * @return: com.zhuoyue.biz.core.api.dto.rep.AppUserRepDTO
     * @author: wjming
     * @Date: 2021-06-28 20:06
     */
    @Transactional(rollbackFor = Exception.class)
    public AppUserRepDTO register(AppUserDO aDo) throws Exception {

        //账号唯一性验证
        accountRepeatedVerification(aDo);

        //安全验证:是否通过APP注册
        registerByApp(aDo);


        //清空此openID相关联的账号信息
        clearOpenId(aDo);

        //添加用户
        saveUserMessage(aDo);
        //配置token和密码进入到do对象
        setTokenToUserDo(aDo);
        updateById(aDo);

        //查询用户
        AppUserDO userDo = getById(aDo.getId());

        //添加家庭
        addHome(userDo.getId(), userDo.getAccount());
        return new AppUserRepDTO().appUserDoToAppUserRepDto(userDo);
    }


    /**
     * 通过账号密码登陆
     *
     * @param requestUserDO
     * @return AppUserRepDTO
     */
    public AppUserRepDTO loginByAccountAndPsd(AppUserDO requestUserDO) {
        AppUserDO selectUserDO = getOne(Wrappers.<AppUserDO>lambdaQuery().eq(AppUserDO::getAccount, requestUserDO.getAccount())
                .eq(AppUserDO::getPassword, requestUserDO.getPassword())
                .eq(AppUserDO::getOrganizationCode, requestUserDO.getOrganizationCode()));

        if (selectUserDO == null) {
            throw new ZyServerException(ErrorType.OBJECT_NOT_FOUND.getCode(), "用户名或密码错误");
        }
        requestUserDO.setId(selectUserDO.getId());
        return loginResponse(requestUserDO, selectUserDO);
    }


    /**
     * 快速登陆业务逻辑
     *
     * @param requestUserDO
     * @return
     */
    public AppUserRepDTO quickLogin(AppUserDO requestUserDO) {
        AppUserDO selectUserDO = getOne(Wrappers.<AppUserDO>lambdaQuery()
                .or(i -> i.eq(AppUserDO::getOpenidApple, requestUserDO.getOpenidApple()))
                .or(i -> i.eq(AppUserDO::getOpenidWeiXin, requestUserDO.getOpenidWeiXin()))
                .or(i -> i.eq(AppUserDO::getOpenidQQ, requestUserDO.getOpenidQQ())));
        if (selectUserDO == null) {
            throw new ZyServerException(ErrorType.OBJECT_NOT_FOUND.getCode(), "未绑定账号");
        }
        requestUserDO.setAccount(selectUserDO.getAccount());
        requestUserDO.setId(selectUserDO.getId());
        return loginResponse(requestUserDO, selectUserDO);
    }


    /**
     * 退出登陆，并且删除用户信息
     *
     * @param user
     */
    public void logoutAndRemoveMessage(User user) {
        redisTemplate.delete(createRedisTokenKey(user.getAccount(), user.getOrgCode()));
        int integer = appUserDao.updateExit(user.getId());
        if (integer < 1) {
            throw new ZyServerException(ErrorType.Service_Fail);
        }
    }

    /**
     * 修改用户信息
     *
     * @param appUser
     * @return
     */
    public AppUserRepDTO updateUserMessage(AppUserDO appUser) {
        boolean b = updateById(appUser);
        if (!b) {
            throw new ZyServerException(ErrorType.Service_Fail);
        }
        return new AppUserRepDTO().appUserDoToAppUserRepDto(getById(appUser.getId()));
    }


    /**
     * 根据手机号和组织编码获取用户信息
     *
     * @param account
     * @param orgCode
     * @return
     */
    public AppUserDO getAccountMessage(String account, String orgCode) {
        AppUserDO userDO = getOne(Wrappers.<AppUserDO>lambdaQuery().eq(AppUserDO::getAccount, account)
                .eq(AppUserDO::getOrganizationCode, orgCode));
        if (userDO == null) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), account + "未注册请先注册！");
        }
        return userDO;
    }

    /**
     * 获取用户昵称
     *
     * @param masterId
     * @return
     */
    public String getUserName(String masterId) {
        return appUserDao.selectNameById(masterId);
    }


    /**
     * @Description: 整合登陆所需的功能，并且返回登陆所需的对象信息
     * @param: requestUserDO
     * @param: selectUserDO
     * @return: com.zhuoyue.biz.core.api.dto.rep.AppUserRepDTO
     * @author:wjming
     * @Date:2021-06-28 16:34
     */
    @Transactional(rollbackFor = Exception.class)
    AppUserRepDTO loginResponse(AppUserDO requestUserDO, AppUserDO selectUserDO) {

        //配置token到do对象
        setTokenToUserDo(requestUserDO);

        requestUserDO.setId(selectUserDO.getId());

        //清空此openID相关联的账号信息
        clearOpenId(requestUserDO);

        boolean b = updateById(requestUserDO);
        if (!b) {
            throw new ZyServerException(ErrorType.Service_Fail);
        }
        //账号顶掉推送
        jPush(requestUserDO, selectUserDO);
        AppUserDO repSelectUserDO = getById(selectUserDO.getId());
        AppUserRepDTO appUserRepDTO = new AppUserRepDTO();
        return appUserRepDTO.appUserDoToAppUserRepDto(repSelectUserDO);

    }


    /**
     * 生成Token
     *
     * @param appUserDO
     * @return
     */
    private String createToken(AppUserDO appUserDO) {
        String id = appUserDO.getId();
        String account = appUserDO.getAccount();
        String orgCode = appUserDO.getOrganizationCode();
        String sourceId = appUserDO.getSourceId();
        Integer sourceType = appUserDO.getSourceType();

        if (StringUtils.isEmpty(id) || StringUtils.isEmpty(account) || StringUtils.isEmpty(orgCode) ||
                StringUtils.isEmpty(sourceId) || sourceType == null
        ) {
            throw new ZyServerException(ErrorType.Service_Fail.getCode(), "数据不全,token生成失败");
        }
        // 为本次登录生成新的token
        Map<String, Object> claims = new HashMap<>(5);
        claims.put("id", id);
        claims.put("account", account);
        claims.put("orgCode", orgCode);
        claims.put("sourceId", sourceId);
        claims.put("sourceType", sourceType);
        String token = jwtOperator.generateToken(claims);
        //将token放到redis
        redisTemplate.opsForValue().set(createRedisTokenKey(appUserDO.getAccount(), orgCode),
                token, 7, TimeUnit.DAYS);
        return token;
    }


    /**
     * 下线推送
     *
     * @param requestUserDO
     * @param dataUserDO
     */
    private void jPush(AppUserDO requestUserDO, AppUserDO dataUserDO) {
        String pushId = dataUserDO.getSourceId();
        if (StringUtils.isNotEmpty(pushId) && !pushId.equals(requestUserDO.getSourceId())) {
            //推送之前先判断一下之前登录的是什么手机,如果是苹果的就需要发送两条消息，如果是安卓的只需要发送一条消息即可
            //手机系统类型 1-苹果手机 2-安卓手机
            String sourceType = dataUserDO.getSourceType() + "";
            try {
                jPushService.send(MessageCode.JPUSH_TITLE,
                        "您的账号被他人登录，请注意账号安全！", MessageCode.ACCOUNT_TYPE,
                        pushId, ExtrasCode.OFFLINE, "", dataUserDO.getId()
                                + "", sourceType, dataUserDO.getOrganizationCode());
            } catch (Exception e) {
                log.warn("推送发送失败 -> 用户账号{} -> 推送id:{}", dataUserDO.getAccount(), pushId);
            }

        }
    }

    /**
     * 创建用户的TokenKey
     *
     * @param account
     * @param orgCode
     * @return
     */
    public String createRedisTokenKey(String account, String orgCode) {
        return account + "_" + orgCode;
    }


    /**
     * 添加默认家庭,App注册时用
     *
     * @param userId
     * @param account
     */
    private void addHome(String userId, String account) {
        // 3.默认新增一个家庭
        HomeDO home = new HomeDO();
        home.setName(account + "的家");
        home.setImgUrl("home_04");
        Boolean addHomeRes = homeService.addHome(home, userId);
        if (!addHomeRes) {
            throw new ZyServerException(ErrorType.Service_Fail.getCode(), "默认家庭创建失败！");
        }
    }

    /**
     * 账号重复性验证
     *
     * @param requestUserDO
     */
    private void accountRepeatedVerification(AppUserDO requestUserDO) {
        int count = count(Wrappers.<AppUserDO>lambdaQuery().eq(AppUserDO::getAccount, requestUserDO.getAccount())
                .eq(AppUserDO::getOrganizationCode, requestUserDO.getOrganizationCode()));
        if (count > 0) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), "此账号已注册请勿重复");
        }
    }

    /**
     * 判断是否通过APP注册,如果是，放行，如果不是，拒绝注册
     *
     * @param requestUserDO
     */
    private void registerByApp(AppUserDO requestUserDO) {
        String tokenKey = SmsTpl.rdsTpl(requestUserDO.getAccount(), requestUserDO.getOrganizationCode()) + "_1";
        Object message = redisTemplate.opsForValue().get(tokenKey);
        if (message == null) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), "请通过APP注册");
        }
        redisTemplate.delete(tokenKey);
        redisTemplate.delete(SmsTpl.rdsTpl(requestUserDO.getAccount(), requestUserDO.getOrganizationCode()));
    }

    /**
     * 将密码和token添加到需要保存的用户对象do中
     *
     * @param requestUserDO
     * @return
     */
    private AppUserDO setTokenToUserDo(AppUserDO requestUserDO) {
        //生成token
        String token = createToken(requestUserDO);
        requestUserDO.setToken(token);
        return requestUserDO;
    }

    /**
     * 如果有快速登陆的信息录入,则先查询其他地方是否有使用。
     * 如果有，使用设置为空
     * 如果没有,返回
     *
     * @param requestUserDO
     */
    private void clearOpenId(AppUserDO requestUserDO) {
        String type = requestUserDO.getType();
        int openIdCount;
        if (StringUtils.isNotEmpty(type)) {
            switch (requestUserDO.getType()) {
                case "0":
                    openIdCount = count(Wrappers.<AppUserDO>lambdaQuery()
                            .eq(AppUserDO::getOpenidWeiXin, requestUserDO.getOpenidWeiXin()));
                    break;
                case "1":
                    openIdCount = count(Wrappers.<AppUserDO>lambdaQuery()
                            .eq(AppUserDO::getOpenidQQ, requestUserDO.getOpenidQQ()));
                    break;
                case "2":
                    openIdCount = count(Wrappers.<AppUserDO>lambdaQuery()
                            .eq(AppUserDO::getOpenidApple, requestUserDO.getOpenidApple()));
                    break;
                default:
                    openIdCount = 0;
                    break;
            }
            if (openIdCount > 0) {
                int i = appUserDao.updateClearOpenId(requestUserDO);
                if (i < 1) {
                    throw new ZyServerException(ErrorType.Service_Fail.getCode(), "清理openId失败");
                }
            }

        }
    }

    /**
     * 保存用户信息
     * 如果保存失败，同时删除redis中生成的token信息
     *
     * @param requestUserDO
     */
    private void saveUserMessage(AppUserDO requestUserDO) {
        boolean save = save(requestUserDO);
        if (!save) {
            redisTemplate.delete(createRedisTokenKey(requestUserDO.getAccount(), requestUserDO.getOrganizationCode()));
            throw new ZyServerException(ErrorType.USER_SAVE_ERROR);
        }
    }


}

